<?php

namespace App\Http\Controllers;

use App\Common\CacheKey;
use App\Http\Resources\Account;
use App\Models\CoinRecord;
use App\Models\ThirdPartyUser;
use App\Models\User;
use App\Services\WeChat;
use App\Support\ImageHandler;
use App\Support\Time;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;

class WeChatController extends Controller
{
    private $wechat;

    /**
     * 注入微信服务
     *
     * @param \App\Services\WeChat $wechat 微信服务
     */
    public function __construct(WeChat $wechat)
    {
        $this->wechat = $wechat;
    }
    
    /**
     * JS SDK 签名
     *
     * @param \Illuminate\Http\Request $request
     * @return mixed
     */
    public function js(Request $request)
    {
        if (! $url = $request->query('url')) {
            return;
        }
        
        $js = $this->wechat->officialAccount()->js;
        $js->setUrl($url);
        
        return $this->wrapData($js->getConfigArray([]));
    }

    /**
     * 请求微信公众号授权，以及授权回调处理
     *
     * @param \Illuminate\Http\Request $request
     * @return mixed
     */
    public function oauth(Request $request)
    {
        // 授权码
        $code = $request->get('code');
        
        $mallUrl = config('mall.url');
        if (! $url = $request->query('redirect_uri')) {
            $url = $mallUrl;
        }

        $oauth = $this->wechat->officialAccount()->oauth;
        
        // 请求微信授权
        if (empty($code)) {
            $callbackUrl = add_query_arg(['redirect_uri' => $url], config('app.url') . config('wechat.oauth.callback'));

            // 解决微信 Android 多次重定向问题，参考：https://github.com/overtrue/socialite/pull/89
            return $oauth->with(['connect_redirect' => 1])->redirect($callbackUrl);
        }

        // 如果是 production 环境，检查域名的合法性
        if (App::environment('production')) {
            // 把授权信息转发给受信任的域名
            if ($response = $this->oauthForTrustDomain($request, $url)) {
                return $response;
            }
            
            if (! is_safe_url($url, $mallUrl)) {
                $url = $mallUrl;
            }
        }

        // 对授权码进行缓存（10分钟），防止微信多次重定向时使用同一授权码，导致报错“code been used”
        if (! Redis::set(CacheKey::wechatOauthCode($code), 1, 'EX', 10 * Time::SECONDS_OF_MINUTE, 'NX')) {
            return;
        }

        // 授权回调处理：用户创建和登录
        $data = $oauth->user()->getOriginal();

        $user = $this->registerUser($data, ThirdPartyUser::TYPE_WX_OFFICIAL);

        $args = ['api_token' => $user->api_token];
        $redirectUrl = str_contains($url, '#') ? ($url . '?' . http_build_query($args)) : add_query_arg($args, $url);

        return redirect($redirectUrl);
    }
    
    /**
     * 移动应用授权登录
     *
     * @param \Illuminate\Http\Request
     * @return \App\Http\Resources\Account
     */
    public function oauthMobile(Request $request)
    {
        $this->validate($request, ['code' => 'required']);
        
        $oauth = $this->wechat->mobileApp()->oauth;
        $data = $oauth->user()->getOriginal();
        
        $user = $this->registerUser($data, ThirdPartyUser::TYPE_WX_MOBILE);
        
        return new Account($user, true);
    }

    /**
     * 注册用户
     *
     * @param array $data
     * @param int $type 第三方登录类型
     * @return \App\Models\User
     */
    protected function registerUser(array $data, $type)
    {
        /** @var \App\Models\ThirdPartyUser $thirdParty */
        $thirdParty = ThirdPartyUser::firstOrNew([
            'type' => $type,
            'openid' => $data['openid'],
        ]);

        if ($thirdParty->exists) {
            return $thirdParty->user;
        }

        $user = User::firstOrNew(['unionid' => $data['unionid']]);

        if (! $user->exists) {
            $user->fill([
                'nickname' => $data['nickname'],
                'gender' => $data['sex'],
            ]);
            
            $path = ImageHandler::downloadImage($data['headimgurl'], upload_path('avatar'));
            if (! empty($path)) {
                $filePath = Storage::path($path);
                image_shrink($filePath);
                
                $user->avatar = $path;
            }
            
            $user->refreshApiToken();
            $user->save();
            
            // 注册奖励
            $this->registerAward($user);
        }
        
        $thirdParty->fill(['user_id' => $user->id])->save();

        return $user;
    }
    
    /**
     * 注册奖励
     *
     * @param \App\Models\User $user
     */
    protected function registerAward($user)
    {
        $award = intval(get_option('register_award'));
        if ($award <= 0) {
            return;
        }
        
        // 新注册用户奖励游戏币
        CoinRecord::create([
            'user_id' => $user->id,
            'coin' => $award,
            'recordable_type' => CoinRecord::TYPE_REGISTER,
            'recordable_id' => $user->id,
        ]);
        
        $user->increment('coin', $award);
    }
    
    /**
     * 把授权信息转发给受信任的域名
     *
     * @param \Illuminate\Http\Request $request
     * @param string $url
     * @return bool|\Illuminate\Http\RedirectResponse
     */
    protected function oauthForTrustDomain($request, $url)
    {
        $domains = config('mall.trust_domains');
        
        foreach ($domains as $domain => $oauthUrl) {
            if (is_safe_url($url, $domain)) {
                return redirect(add_query_arg($request->query(), $oauthUrl));
            }
        }
        
        return false;
    }
}
